package com.cz.basetool.ui_work.thread;

import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.cz.basetool.ui_work.util.log.CZLocalLogPrinter;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 　　　　　　　　┏┓　　　┏┓+ +
 * 　　　　　　　┏┛┻━━━┛┻┓ + +
 * 　　　　　　　┃　　　　　　　┃
 * 　　　　　　　┃　　　━　　　┃ ++ + + +
 * 　　　　　　 ████━████ ┃+
 * 　　　　　　　┃　　　　　　　┃ +
 * 　　　　　　　┃　　　┻　　　┃
 * 　　　　　　　┃　　　　　　　┃ + +
 * 　　　　　　　┗━┓　　　┏━┛
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃ + + + +
 * 　　　　　　　　　┃　　　┃　　　　Code is far away from bug with the animal protecting
 * 　　　　　　　　　┃　　　┃ + 　　　　神兽保佑,代码无bug
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃　　+
 * 　　　　　　　　　┃　 　　┗━━━┓ + +
 * 　　　　　　　　　┃ 　　　　　　　┣┓
 * 　　　　　　　　　┃ 　　　　　　　┏┛
 * 　　　　　　　　　┗┓┓┏━┳┓┏┛ + + + +
 * 　　　　　　　　　　┃┫┫　┃┫┫
 * 　　　　　　　　　　┗┻┛　┗┻┛+ + + +
 * <p>
 * 线程管理类，项目普通耗时操作均使用此类执行线程
 * <p>
 * 如果想要直接调用，而不用线程执行的任何异常，
 * 直接调用{@link #executeTaskWithoutException(Runnable)}或者
 * {@link #executeTaskWithoutException(Runnable, String)}
 * 但是要注意返回布尔类型的结果，如果返回true，说明线程执行成功，反之则失败
 * <p>
 * 如果不想直接调用，需要考虑线程执行异常，一般异常是因为线程池关闭，所以提交的任务不能被执行
 * 调用{@link #executeTask(Runnable, String)}或者
 * {@link #executeTask(Runnable)}
 *
 * @author Done
 * @date 2018/1/31
 */

public class ThreadManager {

    private volatile static ThreadManager instance;

    private ThreadPoolExecutor poolExecutor;

    private LinkedBlockingQueue<Runnable> threadQueue;

    private SmartThreadFactory smartThreadFactory;

    private SmartRejectHandler smartRejectHandler;

    private static final int CORE_SIZE = 50;

    private static final int MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;

    private static final long KEEP_ALIVE_TIME = 0L;
    private long lastExcuteTime = 0;

    public static ThreadManager getInstance() {
        if (null == instance) {
            synchronized (ThreadManager.class) {
                if (null == instance) {
                    instance = new ThreadManager();
                }
            }
        }
        return instance;
    }

    private ThreadManager() {
        threadQueue = new LinkedBlockingQueue<>();
        smartThreadFactory = new SmartThreadFactory();
        smartRejectHandler = new SmartRejectHandler();
        poolExecutor = new ThreadPoolExecutor(CORE_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.MILLISECONDS,
                threadQueue,
                smartThreadFactory,
                smartRejectHandler);
    }

    public synchronized void destroy() {
        if (poolExecutor != null && !poolExecutor.isShutdown()) {
            poolExecutor.shutdown();
        }
        if (threadQueue != null && threadQueue.size() > 0) {
            threadQueue.clear();
        }
        threadQueue = null;
        smartThreadFactory = null;
        smartRejectHandler = null;
        instance = null;
        //Runtime.getRuntime().gc();
    }

    public boolean executeTaskWithoutException(@Nullable Runnable task, @Nullable String threadName) {
        try {
            execute(task, threadName);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * execute task u know
     *
     * @param task task what u want to execute
     * @return if true execute success else failed
     */
    public boolean executeTaskWithoutException(@Nullable Runnable task) {
        try {
            execute(task);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public Future submitTaskWithoutException(@Nullable Callable task) {
        try {
            return submit(task);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public Future submitTaskWithoutException(@Nullable Callable task, @Nullable String threadName) {
        try {
            return submit(task, threadName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void executeTask(@Nullable Runnable task, @Nullable String threadName) throws Exception {
        execute(task, threadName);
    }

    public void executeTask(@Nullable Runnable task) throws Exception {
        execute(task);
    }

    /**
     * 每一秒执行一次
     * @param limitSecond           限制多少秒执行一次
     * @param task
     * @throws Exception
     */
    public void executeTaskEachSecond(int limitSecond,@Nullable Runnable task) throws Exception {
        try {
            long currentTimeMillis = System.currentTimeMillis();
            if(lastExcuteTime == 0){
                execute(task);
                lastExcuteTime = currentTimeMillis;
            }else{
                if(currentTimeMillis - lastExcuteTime > limitSecond*1000){
                    execute(task);
                    lastExcuteTime = currentTimeMillis;
                }else{
                    CZLocalLogPrinter.getInstance().writeLog("时间小于1秒");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            CZLocalLogPrinter.getInstance().writeLog("executeTaskEachSecondError",e.getMessage());
        }
    }


    public Future sumbitTask(@Nullable Callable task, @Nullable String threadName) throws Exception {
        return submit(task, threadName);
    }


    public Future sumbitTask(@Nullable Callable task) throws Exception {
        return submit(task);
    }

    private void execute(Runnable runnable, String threadName) throws Exception {
        smartThreadFactory.setName(threadName);
        poolExecutor.execute(runnable);
    }

    /**
     * execute your task u know
     *
     * @param task task what u want to execute
     * @throws Exception RejectedExecutionHandler {@link SmartRejectHandler}
     */
    private void execute(Runnable task) throws Exception {
        poolExecutor.execute(task);
    }

    private Future submit(Callable task, String threadName) throws Exception {
        smartThreadFactory.setName(threadName);
        return poolExecutor.submit(task);
    }

    /**
     * submit task u know
     *
     * @param task your task
     * @return task's result
     * @throws Exception RejectedExecutionHandler {@link SmartRejectHandler}
     */
    private Future submit(Callable task) throws Exception {
        return poolExecutor.submit(task);
    }

    static class SmartThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private String namePrefix;

        SmartThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                    poolNumber.getAndIncrement() +
                    "-thread-";
        }

        public void setName(String name) {
            if (!TextUtils.isEmpty(name)) {
                namePrefix = name;
            }
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement());
            if (t.isDaemon()) {
                t.setDaemon(false);
            }
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }
    }

    static class SmartRejectHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if (!executor.isShutdown()) {
                executor.execute(r);
            } else {
                throw new RejectedExecutionException("Task " + r.toString() +
                        " rejected from " +
                        executor.toString() + "\n pool is shutdown!");
            }
        }
    }
}
